package org.raymond.iworks.admin.service.impl;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.raymond.iworks.admin.service.ITSysPermissionService;
import org.raymond.iworks.admin.service.ITSysRoleService;
import org.raymond.iworks.admin.service.ITSysUserService;
import org.raymond.iworks.admin.service.cache.IUserCacheService;
import org.raymond.iworks.apis.common.ErrorCode;
import org.raymond.iworks.apis.vo.CacheVO;
import org.raymond.iworks.apis.vo.PageVO;
import org.raymond.iworks.apis.vo.permission.PermissionInfoVO;
import org.raymond.iworks.apis.vo.user.UserInfoVO;
import org.raymond.iworks.apis.vo.user.UserPageVO;
import org.raymond.iworks.apis.vo.user.UserVO;
import org.raymond.iworks.core.constants.LockConstants;
import org.raymond.iworks.apis.exception.BaseException;
import org.raymond.iworks.commons.utils.ErrorUtil;
import org.raymond.iworks.commons.utils.RandomValueUtil;
import org.raymond.iworks.core.constants.SecurityComponets;
import org.raymond.iworks.core.holder.LockHolder;
import org.raymond.iworks.core.holder.UserIdContext;
import org.raymond.iworks.repository.entity.TSysPermission;
import org.raymond.iworks.repository.entity.TSysRole;
import org.raymond.iworks.repository.entity.TSysUser;
import org.raymond.iworks.repository.entity.enums.StateEnum;
import org.raymond.iworks.repository.repository.TSysRoleRepository;
import org.raymond.iworks.repository.repository.TSysUserRepository;
import org.raymond.iworks.repository.utils.RandomUser;
import org.redisson.api.RLock;
import org.redisson.api.RRateLimiter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TSysUserServiceImpl implements ITSysUserService {
    private Argon2PasswordEncoder argon2Encoder = SecurityComponets.ARGON2_PASSWORD_ENCODER;
    @Autowired
    private IUserCacheService iUserCacheService;
    @Autowired
    private LockHolder lockHolder;
    @Autowired
    private ITSysRoleService itSysRoleService;
    private TSysUserRepository tSysUserRepository;
    @Autowired
    private TSysRoleRepository tSysRoleRepository;
    @Autowired
    private ITSysPermissionService itSysPermissionService;

    @Override
    public PageVO<UserVO> page(UserPageVO vo) {
        PageRequest pageRequest = PageRequest.of(vo.getCurrent(), vo.getPageSize());
        TSysUser queryExample = new TSysUser();
        BeanUtils.copyProperties(vo, queryExample);
        queryExample.setCountryCode("+86");
        Example<TSysUser> example = Example.of(queryExample);
        Page<TSysUser> userPage = tSysUserRepository.findAll(example, pageRequest);
        List<UserVO> voList = userPage.get().map(entity->{
            UserVO info = new UserVO();
            BeanUtils.copyProperties(entity, info);
            return info;
        }).collect(Collectors.toList());

        PageVO<UserVO> list = new PageVO<>();
        BeanUtils.copyProperties(vo, list);
        list.setList(voList);
        list.setTotal(userPage.getTotalElements());
        return list;
    }

    @Override
    public boolean createRandomUser(int count) {
        if(count<=0){
            return true;
        }

        List<TSysUser> list = new ArrayList<>();
        for(int i=0; i<=count; i++){
            TSysUser user = buildRandomUser();
            list.add(user);
        }
        tSysUserRepository.saveAll(list);
        return true;
    }

    @Override
    public UserInfoVO current() {
        Long userId = UserIdContext.getUserId();
        if(userId==null){
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        Date now = new Date();
        CacheVO<UserInfoVO> infoCache = iUserCacheService.current(now);
        UserInfoVO oldInfo = null;
        if(infoCache!=null){
            oldInfo = infoCache.getValue();
            Date expireAt = infoCache.getLogicExpireAt();
            if(expireAt.before(now)) {
                return oldInfo;
            }
        }
        // infoCache==null,表示没有缓存,需要设置缓存;
        // infoCache!=null && !expireAt.before(now), 表示逻辑过期,需要更新缓存
        // 高并发情况下,系统响应可能缓慢,用户可能会重复提交,防重复提交也可能不能完全拦截住,因此需要一把针对用户的锁
        RLock lock = lockHolder.lock(LockConstants.LOCK_PREFIX_USER + userId);
        try {
            lock.tryLock(LockConstants.LOCK_TIMEOUT_USER, LockConstants.DEFAULT_UNIT);
            // 锁是针对用户的,高并发情况下,可能会有多个用户同时击穿缓存,因此需要限流,允许较少的并发流量进来访问数据库
            RRateLimiter userRateLimiter = lockHolder.rateLimiter(LockConstants.RATE_LIMITER_USER, LockConstants.RATE_USER, LockConstants.RATE_INTERVAL_USER);
            userRateLimiter.tryAcquire(LockConstants.RATE_ACQUIRE_TIMEOUT_USER, LockConstants.DEFAULT_UNIT);
        } catch (Exception e){
            log.error("{}", ErrorUtil.getErrorLog(e));
            // 锁或者限流器失败,返回过期值
            if(oldInfo!=null){
                return oldInfo;
            }else{
                throw new BaseException(ErrorCode.ERROR);
            }
        }
        UserInfoVO info = new UserInfoVO();

        TSysUser sysUser = tSysUserRepository.getReferenceById(userId);
        UserVO userVo = new UserVO();
        BeanUtils.copyProperties(sysUser, userVo);
        info.setUser(userVo);

        List<TSysRole> roleList = itSysRoleService.getsAssociatedRoleByUserId(userId);
        if(CollectionUtils.isEmpty(roleList)) {
            String message = String.format("用户[%s]未配置任何角色", userId);
            log.warn(message);
            if(oldInfo!=null){
                return oldInfo;
            }else{
                throw new BaseException(ErrorCode.NOT_CONFIGURED, message);
            }
        }
        if(log.isDebugEnabled()) {
            log.debug("用户[{}]配置的角色: {}", userId, roleList.stream().map(TSysRole::getName).collect(Collectors.toSet()));
        }
        Set<Long> roleIds = roleList.stream().mapToLong(e->e.getId()).boxed().collect(Collectors.toSet());
        List<TSysPermission> associatedPermissionByRoleIds = itSysPermissionService.getsAssociatedPermissionByRoleIds(roleIds);
        if(CollectionUtils.isEmpty(associatedPermissionByRoleIds)) {
            String message = String.format("用户[%s]所属角色未配置任何权限", userId);
            log.warn(message);
            if(oldInfo!=null){
                return oldInfo;
            }else{
                throw new BaseException(ErrorCode.NOT_CONFIGURED, message);
            }
        }
        if(log.isDebugEnabled()) {
            log.debug("用户[{}]配置的权限: {}", userId, associatedPermissionByRoleIds.stream().map(TSysPermission::getName).collect(Collectors.toSet()));
        }
        List<PermissionInfoVO> permissionList = associatedPermissionByRoleIds.stream().map(e->{
            PermissionInfoVO perm = new PermissionInfoVO();
            BeanUtils.copyProperties(e, perm);
            return perm;
        }).collect(Collectors.toList());

        info.setPermissions(permissionList);

        infoCache.setValue(info);
        iUserCacheService.cacheUserInfo(infoCache);
        return info;
    }

    @Override
    public String defaultPassword() {
        return argon2Encoder.encode("123456");
    }

    @Override
    public TSysUser buildRandomUser() {
        return RandomUser.create(defaultPassword());
    }
}
